package algorytm;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Random;

import cern.jet.random.engine.RandomGenerator;

import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;

public class Population {
	private final int tournamentSize = 2;
	
	private LinkedList<Chromosom> members;				//raczej ArrayList
	private int graphSize;				//liczba wezlow grafu
	private int amount;					//liczebnosc populacji ???
	
	Population (int amount, int graphSize)
	{
		this.amount=amount;
		this.graphSize=graphSize;
		members=new LinkedList<Chromosom>();
		for (int i=0; i<this.amount; ++i)
		{
			members.add(new Chromosom(this.graphSize));
		}
	}
	
	public LinkedList<Chromosom> getMembers()
	{
		return members;
	}
	
	public void sortMembersByAdjustment(Graph g)
	{
		this.countAdjustmentForAll(g);
		Collections.sort(members, new AdjustmentComparator());
	}
	
	public void countAdjustmentForAll (Graph g)
	{
		for (Chromosom ch : members)
		{
			ch.countAdjustment(g);
		}
	}
	
	public void countEdgesForAll(Graph g) {
		for (Chromosom ch : members)
		{
			ch.countEdges(g);
		}
	}
	
	public double getAdjustmentSum() 
	{
		double sum = 0;
		for (Chromosom ch : members)
		{
			sum += ch.getAdjustment();
		}
		return sum;
	}
	
	
	public void countProbabilityForAll() {
		double sum = this.getAdjustmentSum();
		for (Chromosom ch : members)
		{
			ch.countProbability(sum);
		}
	}
	
	public void sortMembersByProbability() {
		Collections.sort(members, new ProbabilityComparator());
	}
	
	/**
	 * Metoda Selekcji Ruletkowej
	 * @return
	 */																			//z uzyskanej populacji rodzicielskiej _losujemy_ pary osobnikow do przeprowadzenia krzyzowania 
	public LinkedList<Chromosom> selectParentalMembersByRoulette() {			//populacja rodzicielska jest tak samo liczna jak populacja biezaca, 
		Random r = new Random();									//jednak osobniki z o najw. prawdopodbieństwach występują kilkukrotnie
		LinkedList<Chromosom> parentals = new LinkedList<Chromosom>();			
		Chromosom ch = null;
		double rouletteTurnResult = 0;
		for (int i = 0; i < this.amount; i++) {
			rouletteTurnResult = r.nextDouble();				//"zakrecenie kolem ruletki"
			ch = this.getChromosomByRouletteLocation(rouletteTurnResult);
			parentals.add(ch);
		}
		return parentals;											//stary obiekt members juz nie bedzie potrzebny przepisanie do p
	}
	
	private Chromosom getChromosomByRouletteLocation(double rouletteTurnResult) {
		double sum = 0;
		for (Chromosom ch : members) {
			sum += ch.getProbability();
			if (rouletteTurnResult <= sum) { 
				System.out.println(rouletteTurnResult + "<_turnRes  "+ch + " prob:" + ch.getProbability());
				return ch;
			}
		}
		//niemozliwe
		return null;
	}
	
	/**
	 * Metoda selekcji turniejowej. 2 etapy. Najpierw wyłonienie grup (turniei), nastepnie do populacji rodzicielskiej trafiaja 
	 * najlepiej przystosowane z kazdego turnieju
	 * @return
	 */
	public LinkedList<Chromosom> selectParentalMembersByTournament() {				//wzorzec strategia tutaj takze
		LinkedList<Chromosom> tournament = new LinkedList<Chromosom>();
		LinkedList<Chromosom> parentals = new LinkedList<Chromosom>();
		Random random = new Random();
		int randChromosomIndex = 0;
		
		for (int i = 0; i < this.graphSize; i++) {
			tournament.clear();
			for (int j = 0; j < this.tournamentSize; j++) {
				randChromosomIndex = random.nextInt(this.amount);				//czy w turnieju moga byc 2 te same osobniki???
				Chromosom chToTourn =  members.get(randChromosomIndex);									//TODO obecnie taka sytuacja jest dozwolna
				tournament.add(chToTourn);
			}
			System.out.println(tournament);
			parentals.add(Collections.max(tournament, new AdjustmentComparator()));			 
		}
		
		return parentals;				//stary obiekt members juz nie bedzie potrzebny przepisanie do p
		
	}
	
	//TODO
	public LinkedList<Chromosom> createNewGeneration() {						//czy mozna krzyzowac osobnika z samym soba???
		LinkedList<Chromosom> newGenerationMembers = new LinkedList<Chromosom>();
		Random rand = new Random();							//metoda idealna dla wzorca strategii, pod warunkiem przeniesienia metod 
															//crossover do osobnego obiektu 
		int randChIdx = 0;
		for (int i = 0; i < this.graphSize; i++) {
			
			randChIdx = rand.nextInt(this.amount);
			Chromosom parent1 = members.get(randChIdx);
			
			randChIdx = rand.nextInt(this.amount);
			Chromosom parent2 = members.get(randChIdx);
			
			//krzyzowanie  TODO dodanie odpowiedniego konstruktora do klasy Chromosom
			//ew.
			Chromosom child = new Chromosom();
			child.crossoverOneChildSinglePoint(parent1, parent2);					//"clone" i modyfikacja, TODO przy strategii strat.crossover()
			newGenerationMembers.add(child);			
		}
		return newGenerationMembers;		
	}
	
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(this.amount).append("   poAmPrzedGraphSize").append(this.graphSize).append("   ").append("\n").append(this.members);
		return sb.toString();
	}
	
	public static void main(String[] args) {
		Graph <Integer,Integer> g = new UndirectedSparseGraph<Integer,Integer>();
		for (int i = 0; i < 15; i++) {
			g.addVertex(i);
		}
		g.addEdge(1, 1, 2);
		g.addEdge(2, 3, 4);
		g.addEdge(3, 1, 3);
		g.addEdge(4, 1, 4);
		g.addEdge(5, 2, 3);
		g.addEdge(6, 2, 4);
		g.addEdge(7, 1, 2);
		g.addEdge(8, 1, 5);
		g.addEdge(9, 2, 6);
		g.addEdge(10, 3, 8);
		g.addEdge(11, 3, 9);
		g.addEdge(12, 6, 9);
		g.addEdge(13, 2, 12);
		g.addEdge(14, 1, 12);
		g.addEdge(15, 1, 11);
		
				
		Population p = new Population(5, 15);
		p.countEdgesForAll(g);
		p.countAdjustmentForAll(g);
		p.countProbabilityForAll();					//TODO spr. parentals
		System.out.println(p);
		LinkedList<Chromosom> parentals = p.selectParentalMembersByTournament();
		System.out.println(parentals);
	}
	
}


interface CrossingOverAlgorithm {
	public Chromosom crossOver();
	public void lalala();
	
}


